package com.fzz.learnitservice.service.impl;

import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fzz.learnitservice.pojo.dto.WordDataDTO;
import com.fzz.learnitservice.pojo.dto.WordTranslateDTO;
import com.fzz.learnitservice.pojo.po.Word;
import com.fzz.learnitservice.pojo.po.WordExample;
import com.fzz.learnitservice.pojo.po.WordLevel;
import com.fzz.learnitservice.pojo.po.WordPhrase;
import com.fzz.learnitservice.service.*;
import com.fzz.learnitservice.service.impl.task.WordCleanupTask;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.dao.DeadlockLoserDataAccessException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;


@Slf4j
@Service
public class WordImportServiceImpl implements IWordImportService {

    @Autowired private IWordService wordService;
    @Autowired private IWordLevelService wordLevelService;
    @Autowired private IWordExampleService wordExampleService;
    @Autowired private IWordPhraseService wordPhraseService;
    @Autowired private WordCleanupTask wordCleanupTask;

    @Value("${word.translate.ColaKey}")
    private String colaKey;

    private final ObjectMapper objectMapper = new ObjectMapper();
    private final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
    private static final String TRANSLATE_API_URL = "https://luckycola.com.cn/tools/fanyi"; // 中英翻译接口地址

    /** 文件夹名到等级 */
    private static final Map<String, Integer> LEVEL_MAP = Map.ofEntries(
            Map.entry("初中", 2),
            Map.entry("高中", 3),
            Map.entry("4级", 4),
            Map.entry("6级", 5),
            Map.entry("专4", 6),
            Map.entry("专8", 7),
            Map.entry("考研", 8),
            Map.entry("雅思", 9),
            Map.entry("托福", 10)
    );

    /** 并行导入线程数 */
    private static final int THREAD_COUNT = 3;
    private final ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);

    /** ✅ 全局去重缓存（线程安全） */
    private static final Set<String> globalWordSet = ConcurrentHashMap.newKeySet();
    private static final Set<String> globalWordLevelSet = ConcurrentHashMap.newKeySet();
    private static final Set<String> globalWordExampleSet = ConcurrentHashMap.newKeySet();
    private static final Set<String> globalWordPhraseSet = ConcurrentHashMap.newKeySet();

    /**
     * 从资源读取文件，导入所有单词数据
     */
    @Override
    public Map<String, Object> importAllWordsWithStats() {
        long start = System.currentTimeMillis();
        AtomicInteger fileCount = new AtomicInteger();
        AtomicInteger totalWordCount = new AtomicInteger();

        try {
            List<Future<Integer>> futures = new ArrayList<>();
            String[] folders = {"初中","高中","4级","6级","专4","专8","考研","雅思","托福"};

            for (String folder : folders) {
                Integer level = LEVEL_MAP.get(folder);
                if (level == null) continue;

                String pattern = "classpath:wordResources/" + folder + "/*.json";
                Resource[] resources = resourceResolver.getResources(pattern);

                for (Resource res : resources) {
                    futures.add(executor.submit(() -> processFile(res, level, totalWordCount)));
                }
            }

            for (Future<Integer> f : futures) fileCount.addAndGet(f.get());

            long end = System.currentTimeMillis();
            log.info("✅ 导入完成: {} 个文件, {} 个单词, 耗时 {} ms",
                    fileCount.get(), totalWordCount.get(), end - start);

            wordCleanupTask.cleanDuplicates();

            return Map.of(
                    "success", true,
                    "message", "导入成功",
                    "fileCount", fileCount.get(),
                    "wordCount", totalWordCount.get(),
                    "threads", THREAD_COUNT,
                    "time(ms)", end - start
            );

        } catch (Exception e) {
            log.error("❌ 导入失败", e);
            return Map.of("success", false, "message", e.getMessage());
        }
    }

    /**
     * 中英互译
     * @param wordTranslateDTO
     * @return
     */
    @Override
    public String translate(WordTranslateDTO wordTranslateDTO) {
        String fromLang = wordTranslateDTO.getFromLang();
        String toLang = wordTranslateDTO.getToLang();
        if(fromLang.equals(toLang)) {
            throw new RuntimeException("源语言跟翻译语言不能一样");
        }

        OkHttpClient okHttpClient = new OkHttpClient();

        // 构造请求体
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("text", wordTranslateDTO.getText());
        jsonObject.put("ColaKey", this.colaKey);
        jsonObject.put("fromlang", wordTranslateDTO.getFromLang());
        jsonObject.put("tolang", wordTranslateDTO.getToLang());

        RequestBody body = RequestBody.create(jsonObject.toString(), MediaType.parse("application/json"));
        Request request = new Request.Builder()
                .url(TRANSLATE_API_URL)
                .post(body)
                .build();

        try (Response response = okHttpClient.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("翻译接口请求失败：" + response);
            }

            // 解析返回结果
            String responseBody = response.body().string();
            JsonNode root = objectMapper.readTree(responseBody);
            int code = root.path("code").asInt();
            String msg = root.path("msg").asText();
            if(code != 0 || !"翻译成功".equals(msg)) {
                throw new RuntimeException("翻译失败，请联系管理员");
            }

            String result = root.path("data").path("dst").asText();
            return result;

        } catch (IOException e) {
            throw new RuntimeException("翻译失败, " + e);
        }
    }

    // =====================================================
    // 文件任务
    // =====================================================
    @Transactional(rollbackFor = Exception.class)
    public int processFile(Resource resource, Integer level, AtomicInteger totalWordCount) {
        String name = resource.getFilename();
        try (InputStream in = resource.getInputStream()) {
            List<WordDataDTO> words = objectMapper.readValue(in, new TypeReference<>() {});
            log.info("📖 [{}] 包含 {} 个单词", name, words.size());

            int imported = importWordList(words, level);
            totalWordCount.addAndGet(imported);

            log.info("✅ [{}] 导入 {} 个单词", name, imported);
            return 1;
        } catch (Exception e) {
            log.error("⚠️ [{}] 导入失败", name, e);
            return 0;
        }
    }

    // =====================================================
    // 单词批量导入
    // =====================================================
    private int importWordList(List<WordDataDTO> list, Integer level) {
        if (list == null || list.isEmpty()) return 0;

        Map<String, Long> wordIdCache = new ConcurrentHashMap<>();
        List<Word> toInsert = new ArrayList<>();

        // 1️⃣ 获取当前文件中所有单词
        Set<String> candidateWords = list.stream()
                .map(WordDataDTO::getWord)
                .filter(StringUtils::hasText)
                .map(String::trim)
                .collect(Collectors.toSet());

        // 2️⃣ 查询数据库中已存在单词
        List<Word> existing = wordService.list(
                new LambdaQueryWrapper<Word>().in(Word::getWord, candidateWords)
        );
        existing.forEach(w -> {
            wordIdCache.put(w.getWord(), w.getId());
            globalWordSet.add(w.getWord());
        });

        // 3️⃣ 准备新单词
        for (WordDataDTO dto : list) {
            if (!StringUtils.hasText(dto.getWord())) continue;
            String word = dto.getWord().trim();
            if (globalWordSet.add(word)) { // 全局唯一
                toInsert.add(Word.builder()
                        .word(word)
                        .phoneticUs(trim(dto.getUs(), 50))
                        .phoneticUk(trim(dto.getUk(), 50))
                        .meaning(buildMeaning(dto.getTranslations()))
                        .build());
            }
        }

        // 4️⃣ 插入新单词
        if (!toInsert.isEmpty()) {
            saveBatchSafe(wordService, toInsert);
            wordService.list(new LambdaQueryWrapper<Word>()
                    .in(Word::getWord, toInsert.stream().map(Word::getWord).collect(Collectors.toSet()))
            ).forEach(w -> wordIdCache.put(w.getWord(), w.getId()));
        }

        // 5️⃣ 插入关联数据
        insertRelations(list, wordIdCache, level);

        return list.size();
    }

    // =====================================================
    // 关联表处理（防重复）
    // =====================================================
    private void insertRelations(List<WordDataDTO> list, Map<String, Long> idMap, Integer level) {
        List<WordLevel> levelList = new ArrayList<>();
        List<WordExample> exList = new ArrayList<>();
        List<WordPhrase> phList = new ArrayList<>();

        for (WordDataDTO dto : list) {
            String word = dto.getWord();
            if (!StringUtils.hasText(word)) continue;
            Long id = idMap.get(word.trim());
            if (id == null) continue;

            // === WordLevel ===
            String levelKey = id + "_" + level;
            if (level != null && globalWordLevelSet.add(levelKey)) {
                levelList.add(new WordLevel(id, level));
            }

            // === WordExample ===
            if (dto.getSentences() != null) {
                for (WordDataDTO.SentenceDTO s : dto.getSentences()) {
                    if (StringUtils.hasText(s.getSentence()) && StringUtils.hasText(s.getTranslation())) {
                        String key = id + "_" + s.getSentence().trim() + "_" + s.getTranslation().trim();
                        if (globalWordExampleSet.add(key)) {
                            exList.add(WordExample.builder()
                                    .wordId(id)
                                    .sentenceEn(s.getSentence().trim())
                                    .sentenceCn(s.getTranslation().trim())
                                    .build());
                        }
                    }
                }
            }

            // === WordPhrase ===
            if (dto.getPhrases() != null) {
                for (WordDataDTO.PhraseDTO p : dto.getPhrases()) {
                    if (StringUtils.hasText(p.getPhrase()) && StringUtils.hasText(p.getTranslation())) {
                        String key = id + "_" + p.getPhrase().trim() + "_" + p.getTranslation().trim();
                        if (globalWordPhraseSet.add(key)) {
                            phList.add(WordPhrase.builder()
                                    .wordId(id)
                                    .phrase(p.getPhrase().trim())
                                    .meaning(p.getTranslation().trim())
                                    .build());
                        }
                    }
                }
            }
        }

        saveBatchSafe(wordLevelService, levelList);
        saveBatchSafe(wordExampleService, exList);
        saveBatchSafe(wordPhraseService, phList);
    }

    // =====================================================
    // 工具方法
    // =====================================================
    private String trim(String s, int max) {
        if (s == null) return null;
        return s.length() > max ? s.substring(0, max) : s.trim();
    }

    private String buildMeaning(List<WordDataDTO.TranslationDTO> list) {
        if (list == null || list.isEmpty()) return "";
        return list.stream()
                .filter(t -> StringUtils.hasText(t.getTranslation()))
                .map(t -> t.getTranslation().trim() +
                        (StringUtils.hasText(t.getType()) ? "(" + t.getType() + ")" : ""))
                .collect(Collectors.joining("; "));
    }

    /**
     * 批量插入，添加服务降级和死锁检测功能
     * @param service
     * @param list
     * @param <T>
     */
    private <T> void saveBatchSafe(IService<T> service, List<T> list) {
        if (list.isEmpty()) return;
        try {
            service.saveBatch(list, 200);
        } catch (DuplicateKeyException e) {
            log.warn("⚠️ 检测到重复，降级逐条保存");
            for (T t : list) {
                try { service.save(t); } catch (Exception ignored) {}
            }
        } catch (Exception e) {
            if (isCausedBy(e, DeadlockLoserDataAccessException.class)) {
                log.warn("⚠️ 死锁检测，延迟重试");
                sleep(300);
                saveBatchSafe(service, list);
            } else {
                log.error("❌ 批量保存异常", e);
            }
        }
    }

    private boolean isCausedBy(Throwable e, Class<? extends Throwable> c) {
        while (e != null) {
            if (c.isInstance(e)) return true;
            e = e.getCause();
        }
        return false;
    }

    private void sleep(long ms) {
        try { Thread.sleep(ms); } catch (InterruptedException ignored) {}
    }
}
